package com.fattyCorps.battle.service.battle.data;

import cn.hutool.extra.spring.SpringUtil;
import com.fattyCorps.battle.service.battle.Battle;
import com.fattyCorps.battle.service.battle.behavior.Behavior;
import com.fattyCorps.battle.service.battle.behavior.BhvProcessor;
import com.fattyCorps.battle.service.battle.effect.Buff;
import com.fattyCorps.battle.service.battle.effect.Effect;
import com.fattyCorps.common.obj.msg.server.battle.HeroPos;
import com.fattyCorps.common.enums.battle.EBattleAttType;
import com.fattyCorps.common.enums.battle.EBehaviorEffectType;
import com.fattyCorps.common.enums.battle.EBehaviorType;
import com.fattyCorps.common.enums.battle.prop.EStaticProp;
import com.fattyCorps.common.enums.battle.triggerCondition.EBehaviorCondition;
import com.fattyCorps.common.mgr.Cfg;
import com.fattyCorps.common.mgr.GlobalConfig;
import com.fattyCorps.common.obj.config.HeroMould;
import com.fattyCorps.common.obj.config.SkillBehaviorMould;
import com.fattyCorps.common.obj.config.SkillMould;
import com.fattyCorps.common.util.GameCfgUtils;
import com.fattyCorps.common.packet.hero.Hero;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class BattleHero {
    // 英雄原始基础属性数据
    Hero orgHero;
    // 英雄伤害计算时使用的属性
    @Getter
    Hero calcHero = null;
    boolean dirty = true;
    @Getter
    HeroMould mould;
    @Setter
    Battle battle;

    // 站位 1~6
    @Getter
    int standPosNo;
    // hp
    @Getter
    @Setter
    long hp;
    // 能量
    @Getter
    @Setter
    int energy;
    // 死亡
    @Getter
    boolean dead = false;
    // 本回合是否已出手
    @Setter
    @Getter
    boolean roundActive = false;

    // 所属队伍方向
    @Getter
    boolean leftTeam = true;

    // buff列表
    List<Buff> buffs = new ArrayList<>();
    // 所有技能中的行为
    @Getter
    List<Behavior> skillBehaviors = new ArrayList<>();

    Cfg cfg;

    GlobalConfig globalConfig;

    public BattleHero(Hero hero, Battle battle, int standPosNo, boolean leftTeam) {
        this.orgHero = hero;
        this.battle = battle;
        this.standPosNo = standPosNo;
        this.leftTeam = leftTeam;

        cfg = SpringUtil.getBean(Cfg.class);
        globalConfig = SpringUtil.getBean(GlobalConfig.class);

        init();
    }

    // 初始化所有行为
    private void init() {
        for (SkillMould skillMould : orgHero.getSkillComp().getAllSkills()) {
            List<Integer> behaviorIds = GameCfgUtils.splitIdListStr(skillMould.getBehaviorIds());
            for (Integer behaviorId : behaviorIds) {
                SkillBehaviorMould behaviorMould = cfg.getCfg(SkillBehaviorMould.class, behaviorId);
                // 被动增加属性的行为在战斗中不计算行为逻辑
                if (behaviorMould.getType().equals(EBehaviorType.PassiveAddProp)) {
                    continue;
                }

                Behavior bhv = new Behavior(battle, behaviorMould, this, false);
                bhv.init();
                skillBehaviors.add(bhv);
            }
        }

        mould = cfg.getCfg(HeroMould.class, orgHero.getTypeId());
        hp = Math.round(Double.parseDouble(getHero().getPropComp().getMaxHp()));
        energy = globalConfig.getIntValue(GlobalConfig.INIT_ENERGY);
    }

    // 获取英雄当前所有的行为（目前是基础技能行为+buff行为）
    public List<Behavior> getBehaviors() {
        List<Behavior> retList = new ArrayList<>();
        retList.addAll(skillBehaviors);

        // buff的行为
        for (Buff buff : buffs) {
            retList.addAll(buff.getBehaviors());
        }

        return retList;
    }

    // 重写获取英雄属性，每次获取属性前，计算buff加成下的最终属性
    public Hero getHero() {
        if (!dirty) {
            return calcHero;
        }

        dirty = false;

        if (calcHero == null) {
            calcHero = new Hero();
            calcHero.setLv(orgHero.getLv());
            calcHero.setTypeId(orgHero.getTypeId());
            calcHero.setHeroName(orgHero.getHeroName());
            calcHero.setStar(orgHero.getStar());
            calcHero.setId(orgHero.getId());
            calcHero.init();
        }

        String[] propBuffAdd = new String[EStaticProp.values().length];
        for (int i = 0; i < propBuffAdd.length; i++) {
            propBuffAdd[i] = "0";
        }

        for (Buff buff : buffs) {
            for (Behavior behavior : buff.getBehaviors()) {
                for (Effect effect : behavior.getEffects()) {
                    if (effect.getMould().getType().equals(EBehaviorEffectType.StaticProp)) {
                        BigDecimal buffAdd = new BigDecimal(propBuffAdd[effect.getMould().getStaticCalcProp().ordinal()]);
                        BigDecimal addPer = buffAdd.add(new BigDecimal(effect.getMould().getStaticCalcValue()).divide(BigDecimal.valueOf(100)));
                        BigDecimal alreadyAdd = new BigDecimal(propBuffAdd[effect.getMould().getStaticCalcProp().ordinal()]);
                        BigDecimal finalAdd = alreadyAdd.add(addPer);
                        propBuffAdd[effect.getMould().getStaticCalcProp().ordinal()] = finalAdd.toString();
                    }
                }
            }
        }

        for (EStaticProp propType : EStaticProp.values()) {
            String orgProp = orgHero.getPropComp().getProp(propType);
            BigDecimal org = new BigDecimal(orgProp);
            BigDecimal newProp = org.multiply(new BigDecimal(propBuffAdd[propType.ordinal()]).add(BigDecimal.valueOf(1)));
            calcHero.getPropComp().setProp(propType, newProp.toString());
        }

        return calcHero;
    }

    public void addHp(Long addHp) {
        addHp(addHp.toString());
    }

    public void addHp(String addHp) {
        this.hp = Long.valueOf(BigDecimal.valueOf(this.hp).add(new BigDecimal(addHp)).toString());
    }

    public void addEnergy(Integer addEnergy) {
        addEnergy(addEnergy.toString());
    }

    public void addEnergy(String addEnergy) {
        this.energy = Integer.valueOf(BigDecimal.valueOf(this.energy).add(new BigDecimal(addEnergy)).toString());
    }

    public void updateBuffs(BhvProcessor.Event e) {
        for (Buff buff : this.buffs) {
            buff.update(e);
        }

        // 清理无效buff
        for (int i = buffs.size() - 1; i >= 0; i--) {
            if (buffs.get(i).getDuration() == 0) {
                buffs.remove(i);
                this.dirty = true;
            }
        }
    }

    public List<Effect> getEffects() {
        List<Effect> retList = new ArrayList<>();

        for (Behavior behavior : skillBehaviors) {
            for (Effect effect : behavior.getEffects()) {
                retList.add(effect);
            }
        }

        return retList;
    }

    public void addBuff(Buff buff) {
        this.buffs.add(buff);

        // todo: 如果buff是影响静态属性的，则下次计算时需要重新计算属性
        this.dirty = true;
    }

    public boolean hasBuff(int buffId) {
        for (Buff buff : buffs) {
            if (buff.getMould().getId() == buffId) {
                return true;
            }
        }

        return false;
    }

    public boolean hasBuffType(int buffTypeId) {
        for (Buff buff : buffs) {
            if (buff.getMould().getBuffTypeId() == buffTypeId) {
                return true;
            }
        }

        return false;
    }

    public boolean canActive(EBattleAttType type) {
        for (Buff buff : buffs) {
            if (type.equals(EBattleAttType.NorAtt) && !buff.canNorAttack()) {
                return false;
            }

            // todo: 无法技能攻击时，需要将行为改为普通攻击
            if (type.equals(EBattleAttType.CastSkill) && !buff.canCastSkill()) {
                return false;
            }
        }

        return !isDead();
    }

    public void active() {
        EBattleAttType activeType = EBattleAttType.NorAtt;

        Integer skillEnergy = globalConfig.getIntValue(GlobalConfig.SKILL_ENERGY);
        if (energy >= skillEnergy) {
            activeType = EBattleAttType.CastSkill;
        }

        // 判断是否可出手（存在异常buff或死亡就无法出手）
        if (canActive(activeType)) {
            beforeAtt(activeType);
            enterAtt(activeType);
            afterAtt(activeType);
        }
    }

    private void beforeAtt(EBattleAttType attType) {
        // 行动前
        battle.onEvent(EBehaviorCondition.BeforeHeroAtt, this);

        if (attType.equals(EBattleAttType.NorAtt)) {
            battle.onEvent(EBehaviorCondition.BeforeHeroNorAtt, this);
        } else {
            battle.onEvent(EBehaviorCondition.BeforeHeroSkillAtt, this);
        }

        battle.onEvent(EBehaviorCondition.BeforeSelfAtt, this);
    }


    private void enterAtt(EBattleAttType attType) {
        // 行动中
//        battle.onEvent(EBehaviorCondition.HeroActive, this);
        battle.onEvent(EBehaviorCondition.HeroAtt, this);

        if (attType.equals(EBattleAttType.NorAtt)) {
            battle.onEvent(EBehaviorCondition.HeroNorAtt, this);
            battle.onEvent(EBehaviorCondition.SelfNorAtt, this);
        } else {
            battle.onEvent(EBehaviorCondition.HeroSkillAtt, this);
            battle.onEvent(EBehaviorCondition.SelfSkillAtt, this);
        }
    }

    public void afterAtt(EBattleAttType attType) {
        // 行动后
        if (attType.equals(EBattleAttType.NorAtt)) {
            battle.onEvent(EBehaviorCondition.AfterHeroNorAtt, this);
            battle.onEvent(EBehaviorCondition.AfterSelfNorAtt, this);
        } else {
            battle.onEvent(EBehaviorCondition.AfterHeroSkillAtt, this);
            battle.onEvent(EBehaviorCondition.AfterSelfSkillAtt, this);
        }

        battle.onEvent(EBehaviorCondition.AfterHeroAtt, this);
        battle.onEvent(EBehaviorCondition.AfterSelfAtt, this);
    }

    public void underAttack(long damage) {
        battle.onEvent(EBehaviorCondition.BeforeSelfUnderAtt, this);

        this.addHp(-damage);
//        log.info(teamDirection() + " 站位:" + this.standPosNo + " 受到伤害：" + damage + " 剩余hp：" + hp + "/" + getHero().getMaxHp());

        if (hp <= 0) {
            dead();
        }

        battle.onEvent(EBehaviorCondition.AfterSelfUnderAtt, this);
    }

    private void dead() {
        this.dead = true;
        // 死了就没有buff了
        this.energy = 0;
        buffs.clear();
        battle.onEvent(EBehaviorCondition.HeroDead, this);
    }

    public String teamDirection() {
        return this.leftTeam ? "left" : "right";
    }

    public String toString() {
        String ret = " " + teamDirection() + " 站位：" + this.standPosNo;
        if (dead) {
            ret += " 死亡 ";
        }
        return ret;
    }

    public String propsToString() {
        String ret = "";
        for (EStaticProp value : EStaticProp.values()) {
            ret += value.toString() + " " + getHero().getPropComp().getProp(value) + "，";
        }

        return ret;
    }

    public HeroPos getPos() {
        HeroPos pos = new HeroPos();
        pos.setLeftTeam(this.leftTeam);
        pos.setStandPosNo(this.standPosNo);
        return pos;
    }
}
